﻿using System.Collections.Concurrent;
using HelenServer.Grpc.Contracts;

namespace HelenServer.BugEngine.Dal;

internal class FileStorageMigrater
{
    private readonly IGrpcClient<IEnumerable<KeyValuePair<int, IEnumerable<NotifySpiderGrpcModel>>>, GrpcResult<bool>> _migrateQueue = null!;
    private readonly IGrpcClient<int, GrpcResult<IReadOnlyDictionary<int, int[]>>> _fileStorage = null!;
    private readonly Metadata _metadata = new();
    private readonly ILogger<FileStorageMigrater> _logger;

    public FileStorageMigrater(
        IGrpcClient<IEnumerable<KeyValuePair<int, IEnumerable<NotifySpiderGrpcModel>>>, GrpcResult<bool>> migrateQueue,
        IGrpcClient<int, GrpcResult<IReadOnlyDictionary<int, int[]>>> fileStorage,
        Metadata metadata,
        ILoggerFactory factory)
    {
        _migrateQueue = migrateQueue;
        _fileStorage = fileStorage;
        _metadata = metadata;
        _logger = factory.CreateLogger<FileStorageMigrater>();
    }

    public async Task<IDictionary<int, int[]>> MigrateAsync(IDictionary<int, NotifySpiderGrpcModel[]> map, CancellationToken cancellationToken = default)
    {
        try
        {
            var enqueueResult = await this.SpiderInvokeAsync(map, cancellationToken);

            if (!enqueueResult)
            {
                throw new InvalidOperationException();
            }

            var grpcResult = await _fileStorage.InvokeAsync(1, _metadata, cancellationToken);

            var result = grpcResult.Data.ToDictionary(k => k.Key, v => v.Value);

            return result;
        }
        catch (Exception ex)
        {
            throw;
        }
    }

    //public async Task<IDictionary<int, int[]>> MigrateAsync(
    //    IDictionary<int, NotifySpiderGrpcModel[]> map,
    //    CancellationToken cancellationToken = default)
    //{
    //    try
    //    {
    //        var mapsResult = new List<KeyValuePair<int, int[]>>();

    //        var (idmaps, errorBag) = await this.SpiderInvokeAsync(map, cancellationToken);

    //        mapsResult.AddRange(idmaps);

    //        _logger.LogInformation("Spider迁移了{count}个", idmaps.Count);

    //        if (!errorBag.IsEmpty)
    //        {
    //            var error_count = 0;

    //            while (true)
    //            {
    //                _logger.LogInformation("Spider调用失败，失败数量：{count}，迭代次数：{error}", errorBag.Count, ++error_count);

    //                var iterate = errorBag
    //                    .Select(n => new KeyValuePair<int, NotifySpiderGrpcModel[]>(n, map[n]))
    //                    .ToDictionary(k => k.Key, v => v.Value);

    //                var temp = await this.SpiderInvokeAsync(iterate, cancellationToken);

    //                mapsResult.AddRange(temp.idmaps);

    //                _logger.LogInformation("Spider迁移了{count}个", temp.idmaps.Count);

    //                errorBag = temp.errorBag;

    //                if (errorBag.IsEmpty)
    //                {
    //                    break;
    //                }
    //            }
    //        }

    //        return mapsResult.ToDictionary(k => k.Key, v => v.Value);
    //    }
    //    catch (Exception ex)
    //    {

    //        throw;
    //    }
    //}

    private async Task<bool> SpiderInvokeAsync(
        IDictionary<int, NotifySpiderGrpcModel[]> map,
        CancellationToken cancellationToken = default)
    {
        var idmaps = new ConcurrentDictionary<int, int[]>();
        var errorList = new ConcurrentBag<int>();

        var idx = 0;
        var totalCount = map.Count;

        while (idx < totalCount)
        {
            var jump = idx + 200;
            jump = jump < totalCount ? jump : totalCount;
            var spiders = map.Take(idx..jump).Select(n => new KeyValuePair<int, IEnumerable<NotifySpiderGrpcModel>>(n.Key, n.Value.AsEnumerable()));

            var result = await _migrateQueue.InvokeAsync(spiders, _metadata, cancellationToken);

            if (result.Succeed && result.FailedHistory.Count == 0)
            {

            }
            else
            {
                return false;
            }

            idx = jump;
        }

        return true;
        //var parallelOptions = new ParallelOptions { CancellationToken = cancellationToken, MaxDegreeOfParallelism = 8 };

        //await Parallel.ForEachAsync(
        //        map.Keys,
        //        parallelOptions,
        //        async (bugId, cancellationToken) =>
        //        {
        //            Console.WriteLine($"当前工作线程：{Thread.CurrentThread.Name}-{Environment.CurrentManagedThreadId}");

        //            var spiders = map[bugId];
        //            if (spiders.Length == 0)
        //            {
        //                idmaps.GetOrAdd(bugId, Array.Empty<int>());

        //                return;
        //            }

        //            var result = await _fileStorage.InvokeAsync(spiders, _metadata, cancellationToken);

        //            if (result.Succeed && result.FailedHistory.Count == 0)
        //            {
        //                idmaps.GetOrAdd(bugId, result.Data.ToArray());
        //            }
        //            else
        //            {
        //                errorList.Add(bugId);
        //            }

        //            Console.WriteLine($"完成BugId为{bugId}的任务");
        //        });

        //Console.WriteLine("调度结束");

        //return (idmaps, errorList);
    }

    //private Task<(ConcurrentDictionary<int, int[]> idmaps, ConcurrentBag<int> errorBag)> SmartScheduling(IDictionary<int, NotifySpiderGrpcModel[]> map, CancellationToken cancellationToken = default)
    //{
    //    var queue = new Queue<Task<(ConcurrentDictionary<int, int[]> idmaps, ConcurrentBag<int> errorBag)>>();

    //    var jobRange = ThreadPool.ThreadCount / 2 - 1;
    //    var currentCount = map.Count;

    //    var rangeAccessor = map.ToList();

    //    var idx = 0;

    //    while (currentCount > jobRange)
    //    {
    //        var takeJob = map.Take(idx..jobRange).ToDictionary(k => k.Key, v => v.Value);

    //        var task = this.SpiderInvokeAsync(takeJob, cancellationToken);

    //        queue.Enqueue(task);

    //        idx += jobRange + 1;
    //    }

    //    queue.Enqueue(this.SpiderInvokeAsync(map.Take(idx..jobRange).ToDictionary(k => k.Key, v => v.Value), cancellationToken));

    //    return default;
    //}
}